from llama_index.core import SimpleDirectoryReader
from llama_index.core.node_parser import SentenceSplitter

# ===============================
# 1️⃣ 加载本地文档
# ===============================
# SimpleDirectoryReader 是 LlamaIndex 最基础的“数据加载器”，
# 它会自动扫描指定目录（如 ./data）下的所有可读取文件：
# - 文本类文件（.txt、.md、.csv）
# - PDF、Word、HTML 等常见格式（内部会自动解析成纯文本）
#
# 输出结果是一个 List[Document]，每个 Document 代表一篇文档。
# Document 内部包含：
#   - text: 文本内容
#   - metadata: 文件名、路径等元信息
#   - doc_id: 系统自动分配的唯一ID
#
# 这一步是把原始文件“读进 LlamaIndex 世界”。
documents = SimpleDirectoryReader("./data").load_data()



# ===============================
# 2️⃣ 配置文本分割器 SentenceSplitter
# ===============================
# 因为大模型（LLM）一次上下文窗口是有限的（比如 4K、8K tokens），
# 所以一篇长文档必须先被“切块（chunking）”才能进入索引。
#
# SentenceSplitter 就是最常用的“切块器”。
# 它根据句子或换行符等规则，将文档分割成小块（Node）。
# 每个 Node 是 LlamaIndex 的最小检索单元。
#
# 参数详解：
# - chunk_size：每个 chunk 的最大字符数（不是 token，粗略近似）
#   一般 512~1024 比较合适，太大会丢上下文，太小会破碎语义。
#
# - chunk_overlap：相邻块之间保留的“重叠”字符数。
#   这样做是为了保持语义连续性（比如一个句子被切开时）。
#   overlap 越大，语义衔接越好，但索引数据量也会更大。
#
# - separator：句子分割符。默认是换行符 \n，也可以改成句号、逗号等。
#   例如：separator="。" 会按照中文句号分割。
node_parser = SentenceSplitter(
    chunk_size=512,       # 每个块的最大字符数
    chunk_overlap=20,     # 块之间的重叠字符数（保持上下文连贯）
    separator="\n",       # 分割符
)

# ===============================
# 3️⃣ 将文档分割为节点
# ===============================
# get_nodes_from_documents() 会遍历每个 Document，
# 按照 SentenceSplitter 规则切分成多个 Node。
#
# Node 的概念非常重要！每个 Node 是后续向量化、检索的基本单位：
#   - node.text   => 实际文本内容
#   - node.id_    => 唯一节点ID
#   - node.metadata  => 来源信息（文件路径、页码等）
#   - node.relationships  => 与原文、父节点、子节点的层级关系
#
# 换句话说：Node = 拆分后的文本 + 上下文 + 元数据。
nodes = node_parser.get_nodes_from_documents(documents)


# ===============================
# 4️⃣ 查看分割效果
# ===============================
# 这一步我们打印出结果，看看分割是否合理。
# 一般来说：
#   - len(documents) 可能是几篇文件
#   - len(nodes)     可能是几十到几百个小块
#
# 如果分割后的 nodes 太多，说明 chunk_size 太小；
# 如果太少，说明 chunk_size 太大，不利于精准检索。
print(f"\n---->原始文档数: {len(documents)}")
print(f"\n---->分割后的节点数: {len(nodes)}")
for i, node in enumerate(nodes[:3]):  # 显示前10个节点
    print(f"\n-->节点 {i+1}: {node.text[:100]}...")